/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import config.ConfigModels;
import config.Models.Conf_Egg;
import config.Models.Conf_ItemGroup;
import config.Models.Conf_ItemGroup_item;
import config.Models.Conf_Peg;
import config.Models.Conf_PegItemGroup;
import config.Models.Conf_PegLevel;
import db.Redis_Rd;
import db.Redis_W;
import entities.Cote;
import entities.Egg;
import entities.Penguin;
import entities.User;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import libCore.Config;
import share.KeysDefinition;
import libCore.Util;
import org.json.simple.JSONValue;
import share.ShareConstants;
import share.ShareMacros;
import share.Utilities;
import utilities.time.UtilTime;

/**
 *
 * @author LinhTA
 */
public class U_PenguinModel implements BaseModel {

    public String uId;

    public U_PenguinModel(String uid) 
    {
        uId = uid;
    }

    @Override
    public void setDB() 
    {
    }

    @Override
    public void setData(Map<String, String> obj) 
    {
    }

    @Override
    public void getDB(String uid) 
    {
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Static function
    
    public static Map<String, Object> toAMF(Map<String, Penguin> pengs) 
    {
        Map<String, Object> data = new HashMap<String, Object>();

        for (Map.Entry<String, Penguin> entry : pengs.entrySet()) {
            String string = entry.getKey();
            Penguin object = entry.getValue();

            data.put(string, object.toAMF());
        }

        return data;
    }
    
    public static Map<String, Penguin> isNewUser(String uid, String keyPegs, long time) 
    {
        Map<String, Penguin> pegs = new HashMap<String, Penguin>();

        String listPegs = keyPegs;

        int nPeng = Integer.parseInt(Config.getParam("defaultUser", "nDefaultPeng"));
        String listLevels   =   String.valueOf( Config.getParam( "defaultUser", "pengLevels" ) );
        String listTypes    =   String.valueOf( Config.getParam( "defaultUser", "pengTypes" ) );
        
        String[] types      =   listTypes.split("-");
        String[] levels     =   listLevels.split("-");
        
        int counter =   0;
        
        if( types.length != nPeng || levels.length != nPeng )
        {
            System.out.println( "-- Config new penguin user error" );
            return pegs;
        }
        
        //make pengs
        for (int i = 0; i < nPeng; i++) {
            String key  =   KeysDefinition.randomKeySTR();
            Penguin p   =   createPeg( types[i], Integer.parseInt(levels[i]), time );
            if (Redis_W.getInstance().set(key, Util.obj2String(p)) != -1) {
                pegs.put(key, p);
            }
        }

        //make list
        if (pegs.isEmpty()) {
            return pegs;
        }
        else 
        {
            for (Map.Entry<String, Penguin> entry : pegs.entrySet()) {
                String string = entry.getKey();
                Redis_W.getInstance().sadd(listPegs, string);
                counter++;
            }
        }

        System.out.println( "-- U_PenguinModel: isNewUser success with " + counter );
        
        return pegs;
    }
    
    public static Penguin createPeg(String kPeg, int level, long time)
    {
        Penguin peg = new Penguin();

        peg.kind        =   kPeg;
        peg.level       =   level;
        
        Conf_Peg confPeg = ConfigModels.getInstance().getConfPeg(peg.kind);
        int exp = Integer.parseInt( String.valueOf( confPeg.expTrees.get( String.valueOf( peg.level ) ) ));

        peg.exp         =   exp;
        peg.food        =   confPeg.levels.get(String.valueOf(peg.level)).eat.feed;
        peg.timeEat     =   time;
        Conf_PegLevel confPegLevel  =   confPeg.levels.get( String.valueOf( peg.level ) );
        peg.timeSpawn   =   confPegLevel.timeSpawn != -1 ? time : -1;
        
        System.out.println("--------------------Create penguin type " + peg.kind + " at level: " + peg.level);

        return peg;
    }
    
    public static Map<String, Penguin> getDBData(String kpegs)
    {
        Map<String, Penguin> pengs = new HashMap<String, Penguin>();
        String keyPegs = kpegs;

        Set<String> slist = Redis_Rd.getInstance().smember(keyPegs);
        for (int i = 0; i < slist.size(); i++) {
            String kPeg = slist.toArray()[i].toString();
            String peg = Redis_Rd.getInstance().get(kPeg);
            Map<String, Object> data = (HashMap<String, Object>) JSONValue.parse(peg);
            Penguin p = new Penguin();
            p.setData(data);

            pengs.put(kPeg, p);
        }

        return pengs;
    }
    
    public static void pegEat(String kpeng, int exp, long timeEat)
    {
        Penguin peg =   U_PenguinModel.getPeg(kpeng);
        peg.exp     +=  exp;
        peg.food    =   exp;
        peg.timeEat =   timeEat;

        Redis_W.getInstance().set(kpeng, Util.obj2String(peg));
        System.out.println("---------------> Penguin " + kpeng + " type " + peg.kind + " has eat at: " + timeEat);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    public void getPegEggs(Map<Long, Map<String, Egg>> timeEggs, Cote cote, 
            Map<Long, Map<String, Penguin>> listPegHungry, long timeNow)
    {

        listPegHungry.clear();
        for (Map.Entry<String, Penguin> entry : cote.pegs.entrySet()) {
            String kPeg = entry.getKey();
            Penguin peng = entry.getValue();
            // If penguin out of level --> just spawn, do not care about eat
            // Time to eat or peng has never spawn
            updatePenguin(kPeg, peng, timeEggs, cote, listPegHungry, timeNow);
        }
    }

    public void updatePegDB(String kpeng, Penguin peng) 
    {
        Redis_W.getInstance().set(kpeng, Util.obj2String(peng));
    }

    public static Map<String, Penguin> parsePegs(Map<String, Object> obj)
    {
        Map<String, Penguin> pegs = new HashMap<String, Penguin>();

        for (Map.Entry<String, Object> o : obj.entrySet()) {
            String string = o.getKey();
            Object object = o.getValue();

            Penguin penguin = new Penguin();
            penguin.setData(Util.obj2Map(object));
            pegs.put(string, penguin);
        }

        return pegs;
    }

    public boolean pegEatAuto(Map<Long, Map<String, Penguin>> listPeg, Cote cote)
    {
        System.out.println("---------- Check penguin eat auto from server");
        List<Long> timeHungry = new ArrayList<Long>();
        int count = 0;
        for (Map.Entry<Long, Map<String, Penguin>> entry : listPeg.entrySet()) {
            Long long1 = entry.getKey();
            timeHungry.add(long1);
        }
        Collections.sort(timeHungry);
        for (int i = 0; i < timeHungry.size(); i++) {
            long time = timeHungry.get(i);
            Map<String, Penguin> pegs = listPeg.get(time);
            for (Map.Entry<String, Penguin> entry : pegs.entrySet()) {
                String pengId       =   entry.getKey();
                Penguin penguin     =   entry.getValue();
                Conf_Peg confPeg    =   ConfigModels.conf_pegs.get(penguin.kind);
                Conf_PegLevel confPegLevel = confPeg.getPegLevel(penguin.level);

                if (penguin.level >= (confPeg.expTrees.size() - 1)) {
                    System.out.println("------Penguin out of level: eat auto from server");
                } // penguin time eat = -1 that means penguin has hungry for a long time
                else if (cote.food <= 0) {
                    penguin.timeEat = -1;
                    penguin.timeSpawn = -1;
                    penguin.food = 0;
                    updatePegDB(pengId, penguin);
                } else if (cote.food > 0) {
                    count++;
                    
                    int foodCanEat  =   Math.min( cote.food, confPegLevel.eat.feed);
                    cote.food       -=  foodCanEat;
                    long timeEat    =   timeHungry.get(i);
                    
                    // Update penguin exp correctly with number of food ( 1 food = 1 exp)
                    penguin.exp     +=  foodCanEat;

                    // Check for update penguin level
                    int nextLvl = confPeg.expTrees.get(String.valueOf(penguin.level + 1));
                    if (penguin.exp >= nextLvl) {
                        penguin.level += 1;
                        // If penguin time to spawn and never spawn
                        boolean canGiveEgg = confPeg.levels.get(String.valueOf(penguin.level)).timeSpawn != -1;
                        if (penguin.timeSpawn == -1 && canGiveEgg) {
                            // Try to update conf penguin level
                            confPegLevel = confPeg.getPegLevel(penguin.level);
                            penguin.timeSpawn = time - confPegLevel.timeSpawn;
                            System.out.println("----> Now penguin can give egg");
                        }
                    }
                    
                    
                    // Check penguin hungry for a long time
                    
                    if (penguin.timeSpawn != -1 && confPegLevel.timeSpawn == -1) {
                        penguin.timeSpawn = -1;
                    } else if( penguin.timeSpawn == -1 && confPegLevel.timeSpawn != -1)
                        penguin.timeSpawn = timeEat;

                    // Update penguin in penguin hungry list
                    penguin.timeEat =   timeEat;
                    penguin.food    =   foodCanEat;

                    // Update penguin db time eat
                    //pegEat( pengId, confPegLevel.eat.feed, timeEat );
                    updatePegDB(pengId, penguin);

                    // Update penguin in cote
                    cote.pegs.put(pengId, penguin);
                    
                    System.out.println( "--------------------Eat at: " + timeEat );
                }
            }
        }

        if (count > 0) {
            listPeg.clear();
            System.out.println("---------->  " + count + " penguin has eat");
            //System.out.println( count + " : penguin has eaten ----------------left " + cote.food + " ---------------");
            return true;
        }
        return false;
    }
    
    public static Penguin getPeg(String key)
    {
        Penguin p = new Penguin();

        String pengData = Redis_Rd.getInstance().get(key);
        Map<String, Object> data = (HashMap<String, Object>) JSONValue.parse(pengData);

        p.setData(data);

        return p;
    }
    //========================= Hieu ==============================
    public Map<String, Object> pegLevelUp(Map<String, Object> params)
    {

        //U_CoteModel.updatePenguin( params );
        String pengId = params.get(ShareConstants.PENG_ID).toString();
        Penguin peg = U_PenguinModel.getPeg(pengId);

        return peg.toAMF();
    }

    public static Boolean addPenguinDB(String keyPenguinList, String keyPeng, String value)
    {
        return Redis_W.getInstance().sadd(keyPenguinList, keyPeng) == 1
                && Redis_W.getInstance().set(keyPeng, value) >= 0;
    }

    public static Boolean removePenguinDB(String keyPenguinList, String keyPeng)
    {
        return Redis_W.getInstance().srem(keyPenguinList, keyPeng) == 1 && Redis_W.getInstance().del(keyPeng) == 1;
    }

    public static Boolean removePenguinDB(String keyPenguinList, String[] keyPengs)
    {
        return Redis_W.getInstance().srem(keyPenguinList, keyPengs) == keyPengs.length
                && Redis_W.getInstance().del(keyPengs) == keyPengs.length;
    }

    public Map<String, Object> updatePenguin(String pengId, Penguin peng, Map<Long, Map<String, Egg>> timeEggs,
            Cote cote, Map<Long, Map<String, Penguin>> listPegHungry, long timeNow)
    {
        Map<String, Object> result = new HashMap<String, Object>();

        Conf_Peg confPeg = ConfigModels.getInstance().getConfPeg(peng.kind);
        Conf_PegLevel confLevel = confPeg.getPegLevel(peng.level);

        Long timeHungry = peng.timeEat + Utilities.food2TimeHungry( peng.food );
        Long timeNextSpawn = peng.timeSpawn + confLevel.timeSpawn;

        if (timeHungry <= timeNow && (peng.level < (confPeg.expTrees.size() - 1))
            && (timeHungry <= timeNextSpawn || (confLevel.timeSpawn == -1 && peng.timeSpawn == -1))) 
        {
            Map<String, Penguin> mapPeg = new HashMap<String, Penguin>();
            if (listPegHungry.containsKey(timeHungry)) {
                mapPeg = listPegHungry.get(timeHungry);
            }
            mapPeg.put(pengId, peng);
            // Prevent penguin eat many time after hungry for a long time
            if (peng.timeEat == -1) {
                listPegHungry.put(timeNow, mapPeg);
                System.out.println("1/----------------At " + timeNow + " " + pengId + ", type: " + peng.kind + " was hungry");
            } else {
                listPegHungry.put(timeHungry, mapPeg);
                System.out.println("1/----------------At " + timeHungry + " " + pengId + ", type: " + peng.kind + " was hungry");
            }
        }
        else if (((timeHungry > timeNextSpawn) || (peng.level >= (confPeg.expTrees.size() - 1)))
                && confLevel.timeSpawn != -1 && peng.timeSpawn != -1) 
        {
            Long timeGetEgg = timeNow;
            if (timeNow > timeHungry && (peng.level < (confPeg.expTrees.size() - 1))) {
                timeGetEgg = timeHungry;
                Map<String, Penguin> mapPeg = new HashMap<String, Penguin>();
                if (listPegHungry.containsKey(timeHungry)) {
                    mapPeg = listPegHungry.get(timeHungry);
                }

                mapPeg.put(pengId, peng);
                listPegHungry.put(timeHungry, mapPeg);
                System.out.println("2/------------At " + timeHungry + " " + pengId + " was hungry");
            }
            
            String item     =   confPeg.itemTypes.get( String.valueOf( peng.level ));
            Conf_PegItemGroup confItemGroup =   confPeg.getPegItemGroup( peng.level );
            for( Map.Entry<String, String> entry : confItemGroup.groups.entrySet() )
            {
                String value    =   entry.getValue();
                Conf_ItemGroup  group       =   ConfigModels.conf_itemGroups.get( value );
                Conf_ItemGroup_item grpItem =   group.groupItems.get( item );
                if( grpItem.kind.equals( ShareMacros.ITEM_KIND_EGG ) )
                {
                    createEgg( grpItem.kind, pengId, peng, timeEggs, timeNow, 
                            timeHungry, timeNextSpawn, timeGetEgg );
                }
                else if( grpItem.kind.equals( ShareMacros.ITEM_KIND_ITEM ) )
                {
                    
                }
            }
        }

        return result;
    }

    public void createEgg( String itemType, String keyPeng, Penguin peng, Map<Long, Map<String, Egg>> timeEggs,
            long timeNow, Long timeHungry, Long timeNextSpawn, Long timeGetEgg )
    {
        Conf_Peg confPeg = ConfigModels.getInstance().getConfPeg(peng.kind);
        Conf_PegLevel confLevel = confPeg.getPegLevel(peng.level);
        
        Conf_Egg confEgg = ConfigModels.conf_eggs.get(confLevel.egg);
        long timeSpaw = peng.timeSpawn;
        while (timeNextSpawn <= timeGetEgg && timeNow >= timeNextSpawn) {
            timeSpaw    += confLevel.timeSpawn;
            timeNextSpawn += confLevel.timeSpawn;

            int gold    =   confEgg.gold;
            Egg e       =   new Egg();
            e.gold      =   gold;
            e.kind      =   confLevel.egg;
            e.timeBirth =   timeSpaw;
            String keyEgg = U_EggModel.addDBEgg(e);
            System.out.println("----------------At " + timeSpaw + " " + keyPeng + " was spawn new egg " + keyEgg);

            Map<String, Egg> eggData = new HashMap<String, Egg>();
            if (timeEggs.containsKey(timeSpaw)) {
                eggData = timeEggs.get(timeSpaw);
            }

            eggData.put(keyEgg, e);
            timeEggs.put(timeSpaw, eggData);
        }
        peng.timeSpawn = timeSpaw;
        System.out.println( "----------------At " + timeSpaw + " " + keyPeng + " was update time spawn" );
        updatePegDB( keyPeng, peng );
    }
    
    public Map<String, Object> increaseExpPeng( String pengId )
    {
        Map<String, Object> result  =   new HashMap<String, Object>();
        
        Penguin peg         =   U_PenguinModel.getPeg( pengId );
        
        Conf_Peg confPeg    =   ConfigModels.conf_pegs.get(peg.kind);
        Conf_PegLevel confPegLevel = confPeg.getPegLevel(peg.level);
        
        ////////////////////////////////
        // Check for user requirements
        // - Check coin
        UserModel uModel    =   new UserModel( uId );
        User u  =   uModel.getDBUser();
        if( u.coin >= confPegLevel.buyExp.coin )
        {
            int amount          =   confPegLevel.buyExp.exp;
            peg.exp             +=  amount;
        
            // Check for update penguin level
            int nextLvl = confPeg.expTrees.get(String.valueOf(peg.level + 1));
            if (peg.exp >= nextLvl) {
                peg.level   +=  1;
                peg.timeEat =   -1;
                // If penguin time to spawn and never spawn
                boolean canGiveEgg = confPeg.levels.get(String.valueOf(peg.level)).timeSpawn != -1;
                if (peg.timeSpawn == -1 && canGiveEgg) {
                    // Try to update conf penguin level
                    confPegLevel = confPeg.getPegLevel(peg.level);
                    peg.timeSpawn = UtilTime.getTimeNow() - confPegLevel.timeSpawn;
                    System.out.println("----> Now penguin can give egg");
                }
            }
            // Save
            updatePegDB(pengId, peg);

            // Save user
            u.coin              -=  confPegLevel.buyExp.coin;
            uModel.updateDBUser( u );

            String pengStr      =   Redis_Rd.getInstance().get( pengId );
            Map<String, String> mPengData   =   (Map<String, String>) JSONValue.parse( pengStr );
            Map<String, Object> data        =   Util.obj2Map(mPengData);
            data.put( ShareConstants.PENG_ID, pengId );

            result.put( "Penguin", data );
        }
        
        return result;
    }
    
    
    public Map<String, Object> increaseExpPeng( String pengId, int amount )
    {
        Map<String, Object> result  =   new HashMap<String, Object>();
        
        Penguin peg         =   U_PenguinModel.getPeg( pengId );
        peg.exp             +=  amount;
        
        Conf_Peg confPeg    =   ConfigModels.conf_pegs.get(peg.kind);
        Conf_PegLevel confPegLevel = confPeg.getPegLevel(peg.level);
        
        ////////////////////////////////
        // Check for user requirements
        // - Check coin
        UserModel uModel    =   new UserModel( uId );
        User u  =   uModel.getDBUser();
        if( u.coin >= confPegLevel.buyExp.coin )
        {
            // Check for update penguin level
            int nextLvl = confPeg.expTrees.get(String.valueOf(peg.level + 1));
            if (peg.exp >= nextLvl) 
            {
                peg.level       +=  1;
                peg.timeEat     =   -1;
                // If penguin time to spawn and never spawn
                boolean canGiveEgg = confPeg.levels.get(String.valueOf(peg.level)).timeSpawn != -1;
                if (peg.timeSpawn == -1 && canGiveEgg) {
                    // Try to update conf penguin level
                    confPegLevel = confPeg.getPegLevel(peg.level);
                    peg.timeSpawn = UtilTime.getTimeNow() - confPegLevel.timeSpawn;
                    System.out.println("----> Now penguin can give egg");
                }
            }
            // Save
            updatePegDB(pengId, peg);

            // Save user
            u.coin              -=  confPegLevel.buyExp.coin;
            uModel.updateDBUser( u );
            
            String pengStr      =   Redis_Rd.getInstance().get( pengId );
            Map<String, String> mPengData   =   (Map<String, String>) JSONValue.parse( pengStr );
            Map<String, Object> data        =   Util.obj2Map(mPengData);
            data.put( ShareConstants.PENG_ID, pengId );

            result.put( "Penguin", data );
        }
        
        return result;
    }
    
    public Map<String, Object> increaseLevelPeng( String pengId )
    {
        Map<String, Object> result  =   new HashMap<String, Object>();
        
        Penguin peg         =   U_PenguinModel.getPeg( pengId );
        
        Conf_Peg confPeg    =   ConfigModels.conf_pegs.get(peg.kind);
        Conf_PegLevel confPegLevel = confPeg.getPegLevel( (peg.level + 1) );
        
        int amountExp       =   confPeg.expTrees.get( String.valueOf(peg.level+1) ) - peg.exp;
        return increaseExpPeng( pengId, amountExp );
    }
}